//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-02-06     ZhangYihua   first version
//
// Description  : 
//################################################################################

module prbs_gen #(
parameter           PRBS_N                  = 23,
parameter           OUT_DW                  = 8,
parameter           SHIFT_N                 = OUT_DW, // must SHIFT_N<=OUT_DW
parameter           PRBS_INI                = 1'b0,   // 1'b0: all zeros is a normal state and all ones  is a dead state for prbs_dat; 
                                                      // 1'b1: all ones  is a normal state and all zeros is a dead state for prbs_dat; 
parameter           MSB_FIRST               = 1'b1,

// the following parameters are calculated automatically
parameter           PRBS_DW                 = MAX_F(PRBS_N, OUT_DW)
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input               [PRBS_DW-1:0]           cfg_seed,   // tie it to {PRBS_DW{PRBS_INI}}, if not used
input                                       cfg_clr,

output  reg         [PRBS_DW-1:0]           prbs_dat,

output  reg                                 out_vld,
output              [OUT_DW-1:0]            out_dat,
input                                       out_rdy
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        ptrn_dead;

//################################################################################
// main
//################################################################################

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        prbs_dat <=`U_DLY {PRBS_DW{PRBS_INI}};
    end else if (cke==1'b1) begin
        if (out_vld==1'b0)
            prbs_dat <=`U_DLY cfg_seed;
        else if (out_rdy==1'b1)
            prbs_dat <=`U_DLY prbs_nxt_f(prbs_dat, PRBS_N, SHIFT_N, PRBS_INI, MSB_FIRST);
        else
            ;
    end else
        ;
end

assign ptrn_dead = (PRBS_INI==1'b0) ? (&prbs_dat[0+:PRBS_DW]) :
                                    (~(|prbs_dat[0+:PRBS_DW])) ;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        out_vld <=`U_DLY 1'b0;
    end else if (cke==1'b1) begin
        if (cfg_clr==1'b1)
            out_vld <=`U_DLY 1'b0;
        else if ((out_vld==1'b1) && (ptrn_dead==1'b1))
            out_vld <=`U_DLY 1'b0;
        else
            out_vld <=`U_DLY 1'b1;
    end else
        ;
end
assign out_dat = (MSB_FIRST==1'b1) ? prbs_dat[        0+:OUT_DW] :
                                     prbs_dat[PRBS_DW-1-:OUT_DW];
                                     

`include "func_prbs.v"  // refer to prbs_nxt_f()
`include "func_param.v" // refer to MAX_F()

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

a_seed_wrong: assert property (@(posedge clk) disable iff (!rst_n)
    ((cke&(~out_vld)&(~cfg_clr)) |-> (cfg_seed!={PRBS_DW{~PRBS_INI}}))
) else begin
    $error("seed or PRBS_INI is wrong.");
    $stop;
end

// synopsys translate_on
`endif

endmodule
